#ifndef MAIN_NEW_HPP
#define MAIN_NEW_HPP

#include <iostream>
#include "mem.h"
#include <time.h>
#include <math.h>
#include <ios>
#include <bitset>
#include <vector>
#include <cstring>
#include <stdlib.h>
#include "stopwatch.hpp"
#include "local_matrix.hpp"
#include "prime_factorization.hpp"
#include <iterator>

const int IN_PLACE = 0;
const int TMP_MATRIX = 1;
const int TMP_MATRIX_BLOCKED= 2;

void test(int n, int m, int type ){
    Stopwatch stopwatch(true);
    LocalMatrix matrix(n,m);
    double a = stopwatch.getMiliseconds();
    matrix.init();
    double b = stopwatch.getMiliseconds();
    if ( type == IN_PLACE ){
        matrix.transpose_inplace();
    }else if (type == TMP_MATRIX_BLOCKED){
        matrix.transposeByBlocks();
    }else{
        matrix.transpose();
    }
    double c = stopwatch.getMiliseconds();
    std::cout << "creation: " << a << ", init: " << b << ", transposition " << c << " ms" << std::endl;
}

void factorize( int n ){
    std::vector<int> result;
    PrimeFactorization::small_prime_factorize( result, n );
    std::cout << n << " = ";
    std::copy( result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " * ") );
    std::cout << " 1" << std::endl;
}


void show_size( size_t bytes ){
    std::cout << bytes / 1024 << "KB" << std::endl;
    std::cout << bytes / (1024 * 1024) << "MB" << std::endl;
    std::cout << (bytes / 1024) / (1024 * 1024) << "GB" << std::endl;
}

void divide( size_t n, size_t m, size_t proc_npdim, size_t proc_mdim ){
    //(n + proc_npdim - 1) / proc_ndim;
    //(m + proc_mpdim - 1) / proc_mdim;
}


int main_division(){
    size_t size_m = 524288;
    size_t size_n = 1024;
    size_t size_k = 1024;

    size_t mem_size = size_m * size_n * sizeof( float );
    std::cout << "total memory matrix needed :" << mem_size << std::endl;

    int num_procs = 10;
    Partition part0;
    int pn_dim, pm_dim;
    part0.partition( num_procs, size_n, size_m, &pn_dim, &pm_dim );
    size_t local_size_n = (size_n + pn_dim - 1)/ pn_dim;
    size_t local_size_m = (size_m + pm_dim - 1)/ pm_dim;
    size_t local_size_k = (size_k + pn_dim - 1)/ pn_dim;

    std::cout << "local_a: " << local_size_n <<"x"<< local_size_m << " mem: " << ((local_size_n * local_size_m * sizeof(float)) >> 20) << " MB"<< std::endl;
    std::cout << "local_b: " << local_size_m <<"x"<< local_size_k << " mem: " << ((local_size_m * local_size_k * sizeof(float)) >> 20) << " MB"<< std::endl;
    std::cout << "local_c: " << local_size_n <<"x"<< local_size_k << " mem: " << ((local_size_n * local_size_k * sizeof(float)) >> 20) << " MB"<< std::endl;
    std::cout << "temp_a: " << local_size_n <<"x"<< local_size_m << " mem: " << ((local_size_n * local_size_m * sizeof(float)) >> 20) << " MB"<< std::endl;

    size_t total_memory = ((local_size_n * local_size_m * sizeof(float)) >> 20) + ((local_size_m * local_size_k * sizeof(float)) >> 20);
    total_memory += ((local_size_n * local_size_k * sizeof(float)) >> 20) + ((local_size_n * local_size_m * sizeof(float)) >> 20);

    std::cout << "total: " << total_memory << " MB"<< std::endl;

    //std::cout << "memory for each"<< mem_size +  << std::endl;

    return 0;
}


int main_new(){

    size_t huge_size_m = 524288;
    size_t huge_size_n = 1024;
    size_t huge_size = huge_size_m * huge_size_n;
    std::cout << log2( huge_size ) << std::endl;
    size_t bytes = huge_size * 4;
    show_size( bytes );

    std::cout << "total memory :" << std::endl;
    show_size( get_total_system_memory() );

    //std::cout << log2( 524288 * 4) << std::endl;
    //1024 x 524288


    for ( int i = 10; i < 16; ++i){
        //std::cout << "2^" << i << " (" << (1 << i) << "): ";
        //test( 1024, 1 << (i), TMP_MATRIX);
        std::cout << "2^" << i << " (" << (1 << i) << "): ";
        test( 1024, 1 << (i), TMP_MATRIX_BLOCKED);
    }

    factorize( 9 );
    factorize( 1 );
    factorize( 0 );
    factorize( 15 );
    factorize( 27 );
    factorize( 16 );
    factorize( 24 );
    int pn_dim, pm_dim;
    Partition part0;
    part0.partition( 10, huge_size_n, huge_size_m, &pn_dim, &pm_dim );

    Partition part1;
    part1.partition( 24, 32, 32, &pn_dim, &pm_dim);
    Partition part2;
    part2.partition(1024, 32,32, &pn_dim, &pm_dim);
    Partition part3;
    part3.partition(16, 1024, 1024 * 1024, &pn_dim, &pm_dim);

    Partition part4;
    part4.partition(19, 1024, 1024 * 1024, &pn_dim, &pm_dim);
    Partition part5;
    part5.partition(18, 1024, 1024 * 1024, &pn_dim, &pm_dim);

    Partition part6;
    part6.partition(16, 1024, 1024, &pn_dim, &pm_dim);
    Partition part7;
    part7.partition(6, 1024, 1024, &pn_dim, &pm_dim);

    //est( 1000, 1000, IN_PLACE);

    return 0;

        //void * p = mem_malloc_aligned (64, 256 * 16 * 16);
        //std::cout << p << std::endl;
        //mem_free_aligned( p );

        LocalMatrix m( 40, 40 );
        m.init();
        std::cout << "A: " << std::endl;
        m.print();
        std::cout << "---------------------" << std::endl;
        m.transposeByBlocks();
        std::cout << "B: " << std::endl;
        m.print();
        std::cout << "---------------------" << std::endl;
        //m.transpose_inplace();
        //m.print();

        LocalMatrix a( 4, 6 );
        a.init();
        LocalMatrix b( 4, 6 );
        b.init();
        //b.transpose();
        LocalMatrix c( 4, 4 );
        c.init_zero();
        LocalMatrix::mul_transposed( a, b, c );
        c.print();
        std::cout << "---------------------" << std::endl;
        LocalMatrix::mul_transposed( a, b, c );
        c.print();


        //system( "pause" );
        return EXIT_SUCCESS;
}


#endif // MAIN_NEW_HPP
